下方為實際的專案,再引入的 spring-boot package 之中,在 /META-INF 之下 spring.factories 之中,搜尋 FailureAnalyzer 就可以看到 boot 官方實作的各類的 FailureAnalyzer
隨意開任一個 Test ,在其中貼上任一從上述spring.factories 內複製的類別完整路徑,
在 command + 左鍵點擊類別名稱,透過 IDE 反向打開這個類別 (此處筆者的作業系統是 macOS, IDE 是 Intellij ,但 Windows 系統和其他 IDE 也有類似的功能)
首先關注在 NoSuchMethodFailureAnalyzer的類別宣告
根據 AbstractFailureAnalyzer 文件提到, AbstractFailureAnalyzer<T extends Throwable>
內要指定一個 Throwable 的一個子類別,上述指定 NoSuchMethodError; 這裡使用者在自定義上面可以選擇任意的Exception,畢竟所有的Exception 自然是 Throwable 的子類。
protected final <E extends Throwable>E findCause(Throwable failure, Class type)
是一個 final 方法,繼承 AbstractFailureAnalyzer 的類別不得實作
@SuppressWarnings("unchecked")
protected final <E extends Throwable> E findCause(Throwable failure, Class<E> type) {
while (failure != null) {
if (type.isInstance(failure)) {
return (E) failure;
}
failure = failure.getCause();
}
return null;
}
單看這個方法內容,可以看出是將AbstractFailureAnalyzer<T extends Throwable> 中的 T 去做轉型,轉成另一個參數 Class type ,那…這一個類別E又從哪裡提供?
這時就要回到 FailureAnalysis analyze(Throwable failure) ,是來自 FailureAnalyzer 的方法,由 AbstractFailureAnalyzer 本身就有實作 。
@Override
public FailureAnalysis analyze(Throwable failure) {
T cause = findCause(failure, getCauseType());
return (cause != null) ? analyze(failure, cause) : null;
}
從實作內容可以看到,剛剛尋找的來源 Class Type 就是由 getCaueType() 方法所提供。
由 getCauseType() 方法的註解可以看到,回傳由 analyzer 處理的造成錯誤的類型(cause type) 。
那其實使用者只需要自定義唯一的方法,這就是AbstractFailureAnalyzer 的抽象方法
protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause)
這裡我們來臨摹 Spring 官方 NoSuchMethodError 是怎麼寫的
@Override
protected FailureAnalysis analyze(Throwable rootFailure, NoSuchMethodError cause) {
NoSuchMethodDescriptor callerDescriptor = getCallerMethodDescriptor(cause);
if (callerDescriptor == null) {
return null;
}
NoSuchMethodDescriptor calledDescriptor = getNoSuchMethodDescriptor(cause.getMessage());
if (calledDescriptor == null) {
return null;
}
String description = getDescription(callerDescriptor, calledDescriptor);
String action = getAction(callerDescriptor, calledDescriptor);
return new FailureAnalysis(description, action, cause);
}
看樣最後三行,透過 callerDescriptor 、calledDescriptor 去產出錯誤的描述(description)、造成的起因(action),再將這兩者以及 NoSuchMethodError 去產生失敗分析(FailureAnalysis) 。
那 callerDescriptor、calledDescriptor 這兩個又與什麼相關呢?
callerDescriptor 從產生方法 getCallerMethodDescriptor 可以看出他是在分析 NoSuchMethodError 的 StackTraceElement ; 類似地從 getNoSuchMethodDescriptor 以及其之下包覆的方法,可以看到
他是將 NoSuchMethodError 的 Message 去做整理。
參考資料
Create a Custom FailureAnalyzer
https://www.baeldung.com/spring-boot-failure-analyzer